package com.lzw.breadknife.processor;

import com.lzw.breadknife.BindClick;
import com.lzw.breadknife.BindField;
import com.lzw.breadknife.BindLayout;
import com.lzw.breadknife.BindView;
import com.lzw.breadknife.ElementsHelper;
import com.lzw.breadknife.util.ClassValidator;
import com.lzw.breadknife.util.Logger;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;

/**
 * author  : AWeiLoveAndroid
 * e-mail  : lzw460433675@163.com
 * github  : https://gitee.com/AWeiLoveAndroid/BreadKnife
 * project : BreadKnife
 */
@SupportedOptions({"targetGenDir", "version"})
public abstract class AbstractBreadKnifeProcessor extends AbstractProcessor {
    protected Messager messager;
    protected Elements elementUtils;
    protected Map<String, ElementsHelper> mProxyMap = new LinkedHashMap<>();

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        mProxyMap.clear();
        processLayout(messager, annotations, roundEnv);
        processView(messager, annotations, roundEnv);
        processClick(messager, annotations, roundEnv);
        processField(messager, annotations, roundEnv);
        process(messager);
        return true;
    }

    protected abstract void processField(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);


    protected abstract void processClick(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);

    protected abstract void processView(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);

    protected abstract void processLayout(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);

    protected abstract void process(Messager messager);

    protected ProcessingEnvironment processingEnvironment;

    /**
     * 用户自定义的生成路径
     */
    protected String savedPath = "";

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        processingEnvironment = processingEnv;

        messager = processingEnv.getMessager();
        elementUtils = processingEnv.getElementUtils();

        // 从处理环境中获取参数
        Map<String, String> options = processingEnv.getOptions();
        for (Map.Entry<String, String> entry : options.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // key=version, value=1   key=targetGenDir, value=src/main/java
            Logger.logInfo(messager, "从build.gradle中获取参数：key=" + key + ", value=" + value);
            if("targetGenDir".equals(key)) {
                savedPath = value;
            }
        }

    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportType = new LinkedHashSet<>();
        supportType.add(BindLayout.class.getCanonicalName());
        supportType.add(BindView.class.getCanonicalName());
        supportType.add(BindClick.class.getCanonicalName());
        supportType.add(BindField.class.getCanonicalName());
        return supportType;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    protected boolean checkAnnotationValid(Element annotatedElement, Class clazz) {
        if (ClassValidator.isPrivate(annotatedElement)) {
            Logger.logWarning(processingEnvironment, annotatedElement, "%s() must can not be private.", annotatedElement.getSimpleName());
            return false;
        }
        return true;
    }


}
